﻿using AdvVerifyCommonUI.ViewModels;
using AdvVerifyGUI.Config;
using AdvVerifyGUI.Display;
using AdvVerifyGUI.Job;
using AdvVerifyGUI.Repositories;
using AdvVerifyGUI.Services;
using AdvVerifyGUI.Stage;
using AdvVerifyModels.Clients;
using AdvVerifyModels.Entities;
using AdvVerifyModels.Models;
using AdvVerifyModels.Models.Requests.SubstrateTracking;
using AdvVerifyModels.SubstrateTracking;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using NLog;
using Prism.Events;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace AdvVerifyGUI.ViewModels;

public partial class JobViewModel : ObservableObject, INavigationAware
{
    static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    IRegionManager _regionManager;

    //private readonly IMotionControlService _motionControlService;

    [ObservableProperty]
    Visibility showFoupButton = Visibility.Collapsed;

    public ShellConfig ShellConfig { get; set; }

    private readonly ProductRepo _productRepo;
    private readonly RecipeRepo _recipeRepo;
    private readonly RecipeService _recipeService;
    private readonly WaferMapRepo _waferMapRepo;
    //private readonly ILoadWaferUseCase _LoadWaferUseCase;
    private readonly IJobQueue _JobQueue;
    private readonly IWaferServiceClient _waferServiceClient;
    private readonly EFEMViewModel _efemViewModel;
    private readonly IImagingService _imagingService;
    WaferTransferClient _waferTransferClient;

    public EFEMViewModel EFEMViewModel => _efemViewModel;

    public JobViewModel(
        IRegionManager regionManager,
        ShellConfig shellConfig,
        ProductRepo productRepo,
        RecipeRepo recipeRepo,
        RecipeService recipeService,
        IWaferServiceClient waferServiceClient,
        WaferMapRepo waferMapRepo,
        IImagingService imagingService,
        EFEMViewModel efemViewModel,
        IJobQueue jobQueue,
        IEventAggregator eventAggregator,
        WaferTransferClient waferTransferClient)
    {
        _regionManager = regionManager;
        ShellConfig = shellConfig;
        _productRepo = productRepo;
        _recipeRepo = recipeRepo;
        _recipeService = recipeService;
        _waferMapRepo = waferMapRepo;
        //_LoadWaferUseCase = loadWaferUseCase;
        _waferTransferClient = waferTransferClient;
        _JobQueue = jobQueue;
        _waferServiceClient = waferServiceClient;
        _efemViewModel = efemViewModel;
        _imagingService = imagingService;

        Products = _productRepo.GetProducts().Result;
        foreach (var p in Products)
        {
            ProductList.Add(p.Name);
        }
        Recipes = _recipeRepo.GetRecipesByProductId(1).Result;
        WaferMaps = _waferMapRepo.GetWaferMapsByProductId(1).Result;
        SelectProductName = ProductList[0];
        SelectWaferMapName = WaferMapList[0];
        wafermapname = SelectWaferMapName;
    }

    #region Foup Select
    [ObservableProperty]
    int selectedFoupIndex = 0;

    [ObservableProperty]
    int selectedFoupID = 1;

    [ObservableProperty]
    ObservableCollection<Foup> foups = new ObservableCollection<Foup>() { new Foup() { } };

    partial void OnSelectedFoupIndexChanged(int value)
    {
        if (value > -1)
        {
            foreach (var slot in Foups[0].Slots)
            {
                slot.IsSelected = false;
            }
            Foups[0] = _efemViewModel.Foups[value];
            SelectedFoupID = SelectedFoupIndex + 1;
            CarrierId = _efemViewModel.Foups[value].CarrierId;
            SelectAllSlots = false;
        }
    }

    [ObservableProperty]
    bool selectAllSlots;

    partial void OnSelectAllSlotsChanged(bool value)
    {
        foreach (var slot in Foups[0].Slots)
        {
            slot.IsSelected = value;
        }
    }
    #endregion

    public List<ProductOwnerName> Products { get; set; }

    [ObservableProperty]
    List<ProductRecipe> recipes;

    [ObservableProperty]
    List<ProductRecipe> recipeList;

    [ObservableProperty]
    ProductRecipe selectedRecipe;

    [ObservableProperty]
    List<ProductWaferMap> waferMaps;

    [ObservableProperty]
    List<string> productList = new List<string>();

    [ObservableProperty]
    string selectProductName;

    [ObservableProperty]
    List<string> waferMapList = new List<string>();

    [ObservableProperty]
    string selectWaferMapName;
    string wafermapname;

    [ObservableProperty]
    int selectWaferMapIndex;

    [ObservableProperty]
    double jobSchedule = 0;

    [ObservableProperty]
    int completed = 0;

    public ObservableCollection<PJob> JobList { get; set; } = new ObservableCollection<PJob>();

    [ObservableProperty]
    PJob selectedPJob;

    [ObservableProperty]
    string carrierId = "test";

    [ObservableProperty]
    string lotId = "test";

    bool AbortJob = false;

    [ObservableProperty]
    bool noJobRuning = true;

    WaferLoadingQueue? _queue;

    partial void OnSelectProductNameChanged(string value)
    {
        var productId = Products.Find(t => t.Name == SelectProductName).Id;
        WaferMaps = _waferMapRepo.GetWaferMapsByProductId(productId).Result;
        Recipes = _recipeRepo.GetRecipesByProductId(productId).Result;
        WaferMapList = new List<string>();
        foreach (var wafermap in WaferMaps)
        {
            WaferMapList.Add(wafermap.Name);
        }
        SelectWaferMapName = WaferMapList[0];
    }

    partial void OnSelectWaferMapNameChanged(string value)
    {
        var productId = Products.Find(t => t.Name == SelectProductName).Id;
        RecipeList = Recipes.Where(r => r.WaferMapName == SelectWaferMapName).ToList();
    }

    [RelayCommand]
    void JobAdd()
    {
        if (SelectedRecipe == null)
        {
            MessageBox.Show("Please Choose a Recipe which you want to add to job with Slot first!");
            return;
        }
        var job = new PJob
        {
            LoadPortId = SelectedFoupIndex + 1,
            CarrierId = CarrierId,
            RecipeId = SelectedRecipe.Id,
            RecipeName = SelectedRecipe.Name,
            Date = System.DateTime.Now,
            LotId = LotId,
            StateNo = 3,
            Slots = new List<int>()
        };
        foreach (var slot in Foups[0].Slots)
        {
            if (slot.IsSelected) job.Slots.Insert(0, slot.Id);
        }
        if (job.Slots.Count < 1)
        {
            MessageBox.Show("Please Choose Slots which you want to add to job with Recipe first!");
            return;
        }
        JobList.Add(job);
    }

    [RelayCommand]
    void DeleteJob()
    {
        if (SelectedPJob == null)
        {
            MessageBox.Show("Please Choose a Job which you want to Delete First!");
            return;
        }
        JobList.Remove(SelectedPJob);
    }

    [RelayCommand]
    async Task RunJobList()
    {
        Completed = 0;
        JobSchedule = 0;
        if (JobList.Count < 1)
        {
            return;
        }

        NoJobRuning = false;
        AbortJob = false;
        try
        {
            var total = 0;
            foreach (var job in JobList)
            {
                job.StateNo = 1;
                total += job.RunTimes * job.Slots.Count;
            }

            foreach (var job in JobList)
            {
                if (AbortJob)
                {
                    NoJobRuning = true;
                    return;
                }

                for (int i = 0; i < job.RunTimes; i++)
                {
                    if (AbortJob)
                    {
                        NoJobRuning = true;
                        return;
                    }

                    job.StateNo = 2;
                    await _waferTransferClient.OpenFoup(job.LoadPortId);
                    await RunJob(job);
                    if (AbortJob)
                    {
                        NoJobRuning = true;
                        return;
                    }
                    await _waferTransferClient.CloseFoup(job.LoadPortId);
                    Completed += job.Slots.Count;
                    JobSchedule = (100.0 * Completed / total);
                }
                job.StateNo = 0;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
            NoJobRuning = true;
        }
        NoJobRuning = true;
    }

    [RelayCommand]
    async Task Abort()
    {
        MessageBoxResult result = MessageBox.Show("Are you sure Abort Job?", "", MessageBoxButton.YesNo, MessageBoxImage.Question);
        if (result == MessageBoxResult.Yes)
        {
            await AbortNow();
        }
    }

    async Task AbortNow()
    {
        AbortJob = true;
        var q = _queue;
        if (q != null)
        {
            await q.Complete();
        }
        _JobQueue.Pending?.Cancel();
    }

    async Task RunJob(PJob pJob)
    {
        _queue = await _waferTransferClient.LoadAll(new WaferLoadingQueueRequest(pJob.LoadPortId, pJob.Slots, true, true));
        var sw = Stopwatch.StartNew();
        try
        {
            for (int i = 0; i < pJob.Slots.Count && !AbortJob; i++)
            {
                try
                {
                    await _queue.Load();
                }
                catch (Exception ex)
                {
                    AbortJob = true;
                    MessageBox.Show(ex.Message);
                    return;
                }

                IJob job = null;
                var confirmed = true;
                try
                {
                    var recipe = await _recipeService.GetJobPlan(pJob.RecipeId);
                    confirmed = !recipe.Environment.RequireConfirmation;
                    var name = $"{recipe.ProductName}-{recipe.Name}-{DateTime.Now:yyyy'-'MM'-'dd'_'HH'-'mm'-'ss}";
                    recipe.Name = name;
                    job = _JobQueue.UnsafeRun(recipe, new JobRunOptions
                    {
                        Id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                        OutputDirectory = Path.Join(@"D:\results", name),
                        SuppressImagesAfterCompletion = true
                    });
                    await job.Completion;
                }
                catch (Exception ex)
                {
                    Logger.Error(ex, "job run error");

                    switch (ex)
                    {
                        case AlignmentException:
                            // evict the wafer but continue
                            MessageBox.Show(ex.Message);
                            break;
                        case OperationCanceledException:
                            MessageBox.Show("Job canceled");
                            break;
                        case ThresholdException:
                            break;
                        default:
                            await _imagingService.UnsafeUseHigh(true);
                            await AbortNow();
                            MessageBox.Show(ex.Message);
                            return;
                    }
                }

                await job.Confirmation;
                await _imagingService.UnsafeUseHigh(true);

                await _queue.Unload();
            }
            await _queue.AwaitCompletion();
            Logger.Info($"Total took {sw.ElapsedMilliseconds}ms.");
        }
        finally
        {
            _queue = null;
        }
    }

    #region INavigationAware
    public bool IsNavigationTarget(NavigationContext navigationContext) => true;

    public void OnNavigatedFrom(NavigationContext navigationContext)
    {
        foreach (var slot in Foups[0].Slots)
        {
            slot.IsSelected = false;
        }
        SelectAllSlots = false;
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        Products = _productRepo.GetProducts().Result;
        ProductList.Clear();
        foreach (var p in Products)
        {
            ProductList.Add(p.Name);
        }
        var productId = Products.Find(t => t.Name == SelectProductName).Id;
        WaferMaps = _waferMapRepo.GetWaferMapsByProductId(productId).Result;
        Recipes = _recipeRepo.GetRecipesByProductId(productId).Result;
        WaferMapList = new List<string>();
        foreach (var wafermap in WaferMaps)
        {
            WaferMapList.Add(wafermap.Name);
        }
        SelectWaferMapName = WaferMapList[0];
        if (_efemViewModel.Foups?.Count >= SelectedFoupID)
        {
            Foups[0] = _efemViewModel.Foups[SelectedFoupIndex];
        }
    }

    #endregion
}